﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Devv.Core.Erroo;
using Devv.FreshTracker.Entity;

namespace Devv.FreshTracker
{
	public class FreshBooks
	{
		#region Properties

		public static String REFER_URL = "https://devv.freshbooks.com/refer/www";
		public static String DEFAULT_API_URL = "https://#COMPANY#.freshbooks.com/api/2.1/xml-in";
		public static String DEFAULT_XMLNS = "http://www.freshbooks.com/api/";
		
		#endregion

		#region Authentication

		public static Int64 TestLogin(String url, String ApiToken)
		{
			XmlDocument xml = CreateRequest("staff.current");
			XmlDocument response = GetResponse(xml, true);
			Int64 userId = 0;

			if (CheckResponse(response))
			{
				XmlNamespaceManager ns = new XmlNamespaceManager(response.NameTable);
				ns.AddNamespace("ns", FreshBooks.DEFAULT_XMLNS);

				try
				{
					userId = Convert.ToInt64(response.SelectSingleNode("//ns:staff_id", ns).InnerText);

					Maintenance.Log("API login tested successfully at " + url);
				}
				catch (Exception ex)
				{
					ErrorHandler.Handle(ex);
				}
				
			}

			return userId;
		}

		#endregion

		#region Get clients

		public static ClientCollection GetClient()
		{
			ClientCollection result = new ClientCollection();

			result.Add(Client.Internal());

			XmlDocument xml = CreateRequest("client.list");

			XmlDocument response = GetResponse(xml);
			XmlNodeList nodes = response.GetElementsByTagName("client");

			foreach (XmlNode n in nodes)
			{
				result.Add(new Client(n));
			}

			Maintenance.Log("Got " + result.Count + " clients from FreshBooks.");

			return result;
		}

		#endregion

		#region Get projects

		public static ProjectCollection GetProject()
		{
			return GetProject(null);
		}

		public static ProjectCollection GetProject(Client client)
		{
			ProjectCollection result = new ProjectCollection();

			XmlDocument xml = CreateRequest("project.list");

			if (client != null)
			{
				if (client.Id > 0)
				{
					AddFilter(xml.DocumentElement, "client_id", client.Id);
				}
				else
				{
					AddFilter(xml.DocumentElement, "client_id", String.Empty);
				}
			}
			
			XmlDocument response = GetResponse(xml);
			XmlNodeList nodes = response.GetElementsByTagName("project");

			foreach (XmlNode n in nodes)
			{
				result.Add(new Project(n));
			}

			Maintenance.Log("Got " + result.Count + " projects from FreshBooks.");

			return result;
		}

		#endregion

		#region Get tasks

		public static TaskCollection GetTasks()
		{
			return GetTasks(null);
		}

		public static TaskCollection GetTasks(Project project)
		{
			TaskCollection result = new TaskCollection();

			XmlDocument xml = CreateRequest("task.list");

			if (project != null && project.Id > 0)
			{
				AddFilter(xml.DocumentElement, "project_id", project.Id);
			}
			
			XmlDocument response = GetResponse(xml);
			XmlNodeList nodes = response.GetElementsByTagName("task");

			foreach (XmlNode n in nodes)
			{
				result.Add(new Task(n, project));
			}

			Maintenance.Log("Got " + result.Count + " tasks for project " + (project != null ? project.Id.ToString() : " ALL ") + " from FreshBooks.");

			return result;
		}

		#endregion

		#region Get time entries

		public static Session GetLastSession()
		{
			XmlDocument xml = CreateRequest("time_entry.list");

			AddFilter(xml.DocumentElement, "date_from", DateTime.Now.AddDays(-7));
			AddFilter(xml.DocumentElement, "date_to", DateTime.Now);

			XmlDocument response = GetResponse(xml);

			if (!CheckResponse(response))
			{
				return null;
			}

			Session result = new Session(response);

			Maintenance.Log("Got session " + result.Id + " from FreshBooks.");

			return result;
		}

		#endregion

		#region Send to FreshBooks

		public static Boolean UploadSession(Session s)
		{
			XmlDocument xml = CreateRequest("time_entry.create");
			XmlNode node = xml.CreateElement("time_entry");

			AddFilter(node, "task_id", s.Task.Id);
			AddFilter(node, "project_id", s.Task.Project.Id);
			AddFilter(node, "hours", s.Hours);
			AddFilter(node, "date", s.Date);
			AddFilter(node, "notes", s.Notes);

			xml.DocumentElement.AppendChild(node);

			XmlDocument response = GetResponse(xml);

			if (!CheckResponse(response))
			{
				Maintenance.Log("Could not upload session " + s + " to FreshBooks.");

				return false;
			}

			Maintenance.Log("Uploaded session " + s + " to FreshBooks.");

			return true;
		}

		#endregion

		#region Helpers

		private static XmlDocument CreateRequest(String method)
		{
			XmlDocument xml = new XmlDocument();
			XmlNode node = xml.CreateElement("request");
			XmlAttribute atr = xml.CreateAttribute("method");

			atr.Value = method;
			node.Attributes.Append(atr);
			xml.AppendChild(node);

			return xml;
		}

		private static XmlDocument GetResponse(XmlDocument xml, Boolean errorBypass = false)
		{
			XmlDocument result = new XmlDocument();

			try
			{
				HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Options.ApiUrl);

				req.AllowWriteStreamBuffering = true;
				req.KeepAlive = true;
				req.UseDefaultCredentials = false;
				req.Credentials = new NetworkCredential(Options.ApiToken, String.Empty);
				req.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(Options.ApiToken)));
				req.Timeout = 20000;
				req.Method = "POST";
				req.ContentType = "text/xml";

				using (StreamWriter sw = new StreamWriter(req.GetRequestStream()))
				{
					xml.Save(sw);
					sw.Close();

					using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
					{
						using (StreamReader sr = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
						{
							result.LoadXml(sr.ReadToEnd());
							sr.Close();
						}

						resp.Close();
					}
				}
			}
			catch (Exception ex)
			{
				if (errorBypass)
				{
					return result;
				}

				String msg = ex.Message;

				if (msg.Contains("401") || msg.Contains("403"))
				{
					MessageBox.Show(Messages.ErrorRequestForbidden, Messages.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
				else
				{
					MessageBox.Show(Messages.ErrorWebRequest, Messages.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}

			return result;
		}

		private static Boolean CheckResponse(XmlDocument xml)
		{
			if (xml == null || xml.DocumentElement == null)
			{
				return false;
			}

			XmlAttribute atr = xml.DocumentElement.Attributes["status"];

			if (atr == null || atr.Value.ToLower() != "ok")
			{
				return false;
			}

			return true;
		}

		private static void AddFilter(XmlNode node, String filter, DateTime value)
		{
			AddFilter(node, filter, value.ToString("yyyy-MM-dd"));
		}

		private static void AddFilter(XmlNode node, String filter, Double value)
		{
			AddFilter(node, filter, value.ToString("0.00"));
		}

		private static void AddFilter(XmlNode node, String filter, Int64 value)
		{
			AddFilter(node, filter, value.ToString());
		}

		private static void AddFilter(XmlNode node, String filter, String value)
		{
			XmlNode f = node.OwnerDocument.CreateElement(filter);
			f.InnerText = value;

			node.AppendChild(f);
		}

		#endregion
	}
}